///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#ifndef __Renderable_H__
#define __Renderable_H__

#include "OgrePrerequisites.h"
#include "OgreCommon.h"

#include "OgreRenderOperation.h"
#include "math/matrix4.h "
#include "OgreMaterial.h"
#include "math/plane.h"
#include "OgreGpuProgram.h"
#include "math/vector4.h"
#include "core/exception.h"
#include "OgreUserObjectBindings.h"

namespace Ogre {

	
	
	
    class _OgreExport Renderable
    {
	public:
		
		class RenderSystemData {}; 
    public:
		Renderable() : mPolygonModeOverrideable(true), mUseIdentityProjection(false), mUseIdentityView(false), mRenderSystemData(NULL) {}
        
        virtual ~Renderable() 
		{
			if (mRenderSystemData)
			{
				delete mRenderSystemData;
				mRenderSystemData = NULL;
			}			
		}
        
        virtual const MaterialPtr& getMaterial(void) const = 0;
        
        virtual Technique* getTechnique(void) const { return getMaterial()->getBestTechnique(0, this); }
        
        virtual void getRenderOperation(RenderOperation& op) = 0;

		
		virtual bool preRender(SceneManager* sm, RenderSystem* rsys)
                { (void)sm; (void)rsys; return true; }

		
		virtual void postRender(SceneManager* sm, RenderSystem* rsys)
                { (void)sm; (void)rsys; }

        
        virtual void getWorldTransforms(Matrix4* xform) const = 0;

        
        virtual unsigned short getNumWorldTransforms(void) const { return 1; }

        
        void setUseIdentityProjection(bool useIdentityProjection)
        {
            mUseIdentityProjection = useIdentityProjection;
        }

        
        bool getUseIdentityProjection(void) const { return mUseIdentityProjection; }

        
        void setUseIdentityView(bool useIdentityView)
        {
            mUseIdentityView = useIdentityView;
        }

        
        bool getUseIdentityView(void) const { return mUseIdentityView; }

		
		virtual Real getSquaredViewDepth(const Camera* cam) const = 0;

        
        virtual const LightList& getLights(void) const = 0;

        
        virtual bool getCastsShadows(void) const { return false; }

        
        void setCustomParameter(size_t index, const Vector4& value) 
        {
            mCustomParameters[index] = value;
        }

		
        void removeCustomParameter(size_t index)
        {
            mCustomParameters.erase(index);
        }

		
        bool hasCustomParameter(size_t index) const
        {
            return mCustomParameters.find(index) != mCustomParameters.end();
        }

        
        const Vector4& getCustomParameter(size_t index) const
        {
            CustomParameterMap::const_iterator i = mCustomParameters.find(index);
            if (i != mCustomParameters.end())
            {
                return i->second;
            }
            else
            {
                PHILO_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
                    "Parameter at the given index was not found.",
                    "Renderable::getCustomParameter");
            }
        }

        
        virtual void _updateCustomGpuParameter(
            const GpuProgramParameters::AutoConstantEntry& constantEntry,
            GpuProgramParameters* params) const
        {
            CustomParameterMap::const_iterator i = mCustomParameters.find(constantEntry.data);
            if (i != mCustomParameters.end())
            {
                params->_writeRawConstant(constantEntry.physicalIndex, i->second, 
					constantEntry.elementCount);
            }
        }

		
		virtual void setPolygonModeOverrideable(bool override)
		{
			mPolygonModeOverrideable = override;
		}

		
		virtual bool getPolygonModeOverrideable(void) const
		{
			return mPolygonModeOverrideable;
		}

		
		virtual void setUserAny(const Any& anything) { getUserObjectBindings().setUserAny(anything); }

		
		virtual const Any& getUserAny(void) const { return getUserObjectBindings().getUserAny(); }

		
		UserObjectBindings&	getUserObjectBindings() { return mUserObjectBindings; }

		
		const UserObjectBindings& getUserObjectBindings() const { return mUserObjectBindings; }


		
		class Visitor
		{
		public:
            
            virtual ~Visitor() { }
			
			virtual void visit(Renderable* rend, ushort lodIndex, bool isDebug, 
				Any* pAny = 0) = 0;
		};

		
		virtual RenderSystemData * getRenderSystemData() const 
		{ 
			return mRenderSystemData; 
		}
		
		virtual void setRenderSystemData(RenderSystemData * val) const
		{ 
			mRenderSystemData = val; 
		}


    protected:
        typedef map<size_t, Vector4>::type CustomParameterMap;
        CustomParameterMap mCustomParameters;
		bool mPolygonModeOverrideable;
        bool mUseIdentityProjection;
        bool mUseIdentityView;
		UserObjectBindings mUserObjectBindings;		 // User objects binding.
		mutable RenderSystemData * mRenderSystemData;// this should be used only by a render system for internal use
	};

	
	


}

#endif //__Renderable_H__
